package cn.conac.rc.workflow.manager.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
 * Created by haocm on 2017-4-6.
 */
@Service
public abstract class ProcessCommonMngImpl {

	//工作流仓储服务
	@Autowired
	private RepositoryService repositoryService;
	//工作流运行服务
	@Autowired
	private RuntimeService runtimeService;
	//工作流任务服务
	@Autowired
	private TaskService taskService;
	//工作流表单服务
	@Autowired
	private FormService formService;
	//工作流用户服务
	@Autowired
	private IdentityService identityService;
	//工作流历史数据服务
	@Autowired
	private HistoryService historyService;
	//工作流管理服务
	@Autowired
	private ManagementService managementService;
	
	/**
	 * 通过清单子id号和系统类别获取活动任务
	 * @param childItemId
	 * @param sysType
	 * @return
	 */
	public Task findTaskByVariable(Integer childItemId,String sysType){
		List<Task> tasks = taskService.createTaskQuery().processVariableValueEquals("childItemId", childItemId)
		                                                .processVariableValueEquals("sysType",sysType).list();
		if(tasks != null && tasks.size() > 0){
			return tasks.get(0);
		}else{
			return null;
		}
	}
	
	/**
	 * 事项审核挂起
	 * @param childItemId
	 * @param sysType
	 * @return 
	 */
	public void suspendProcess(Integer childItemId,String sysType){
		Task task = this.findTaskByVariable(childItemId, sysType);
		if(task != null && task.getId() != null){
			task.setDescription("挂起");
			taskService.saveTask(task);
		}
	}
	
	/**
	 * 提交当前任务到下一节点，如果流程结束，则返回true，否则返回false
	 * @param childItemId
	 * @param sysType
	 * @return
	 */
	public boolean commitProcess(Integer childItemId,String sysType){
		Task task = this.findTaskByVariable(childItemId, sysType);
		if(task != null && task.getId() != null){
			String processInstanceId = task.getProcessInstanceId();
			taskService.complete(task.getId());
			return isProcessInstanceEnded(processInstanceId);
		}else{
			return true;
		}
		
	}
	
	/**
	 * 驳回当前任务到填报节点
	 * @param childItemId
	 * @param sysType
	 */
	public void backProcess(Integer childItemId,String sysType){
		Task task = this.findTaskByVariable(childItemId, sysType);
		Map<String, Object> processVariables = this.getRuntimeService().getVariables(task.getProcessInstanceId());
		if(task != null && task.getId() != null){
			turnTransition(task.getId(),(String)processVariables.get("startActivityId"));
		}
	}
	
	/**
	 * 通过流程实例号判断流程是否结束
	 * @param instanceId
	 * @return
	 */
	public boolean isProcessInstanceEnded(String instanceId){
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()
				                           .processInstanceId(instanceId)//使用流程实例ID查询
				                           .singleResult();
		if(pi==null){
		  return true;
		}else{
		  return false;
		}
	}
	
	/**
	 * 流程转向操作
	 * 
	 * @param taskId
	 *            当前任务ID
	 * @param activityId
	 *            目标节点任务ID
	 *            流程变量
	 * @throws Exception
	 */
	private void turnTransition(String taskId, String activityId){
		// 当前节点
		ActivityImpl currActivity = findActivitiImpl(taskId, null);
		// 清空当前流向
		List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);

		// 创建新流向
		TransitionImpl newTransition = currActivity.createOutgoingTransition();
		// 目标节点
		ActivityImpl pointActivity = findActivitiImpl(taskId, activityId);
		// 设置新流向的目标节点
		newTransition.setDestination(pointActivity);

		// 执行转向任务
		taskService.complete(taskId);
		// 删除目标节点新流入
		pointActivity.getIncomingTransitions().remove(newTransition);

		// 还原以前流向
		restoreTransition(currActivity, oriPvmTransitionList);
	}
	
	/**
	 * 还原指定活动节点流向
	 * 
	 * @param activityImpl
	 *            活动节点
	 * @param oriPvmTransitionList
	 *            原有节点流向集合
	 */
	private void restoreTransition(ActivityImpl activityImpl,
			List<PvmTransition> oriPvmTransitionList) {
		// 清空现有流向
		List<PvmTransition> pvmTransitionList = activityImpl
				.getOutgoingTransitions();
		pvmTransitionList.clear();
		// 还原以前流向
		for (PvmTransition pvmTransition : oriPvmTransitionList) {
			pvmTransitionList.add(pvmTransition);
		}
	}
	
	/**
	 * 清空指定活动节点流向
	 * 
	 * @param activityImpl
	 *            活动节点
	 * @return 节点流向集合
	 */
	private List<PvmTransition> clearTransition(ActivityImpl activityImpl) {
		// 存储当前节点所有流向临时变量
		List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
		// 获取当前节点所有流向，存储到临时变量，然后清空
		List<PvmTransition> pvmTransitionList = activityImpl
				.getOutgoingTransitions();
		for (PvmTransition pvmTransition : pvmTransitionList) {
			oriPvmTransitionList.add(pvmTransition);
		}
		pvmTransitionList.clear();

		return oriPvmTransitionList;
	}
	
	/**
	 * 根据任务ID和节点ID获取活动节点 <br>
	 * 
	 * @param taskId
	 *            任务ID
	 * @param activityId
	 *            活动节点ID <br>
	 *            如果为null或""，则默认查询当前活动节点 <br>
	 *            如果为"end"，则查询结束节点 <br>
	 * 
	 * @return
	 * @throws Exception
	 */
	private ActivityImpl findActivitiImpl(String taskId,
			String activityId){
		// 取得流程定义
		ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);

		// 获取当前活动节点ID
		if (activityId == null || activityId.isEmpty()) {
			activityId = findTaskById(taskId).getTaskDefinitionKey();
		}

		// 根据流程定义，获取该流程实例的结束节点
		if (activityId.equals("endNode")) {
			for (ActivityImpl activityImpl : processDefinition.getActivities()) {
				List<PvmTransition> pvmTransitionList = activityImpl
						.getOutgoingTransitions();
				if (pvmTransitionList.isEmpty()) {
					return activityImpl;
				}
			}
		}

		// 根据节点ID，获取对应的活动节点
		ActivityImpl activityImpl = ((ProcessDefinitionImpl) processDefinition)
				.findActivity(activityId);

		return activityImpl;
	}
	
	/**
	 * 根据任务ID获取流程定义
	 * 
	 * @param taskId
	 *            任务ID
	 * @return
	 * @throws Exception
	 */
	public ProcessDefinitionEntity findProcessDefinitionEntityByTaskId(
			String taskId){
		// 取得流程定义
		ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
				.getDeployedProcessDefinition(findTaskById(taskId)
						.getProcessDefinitionId());
		return processDefinition;
	}
	
	/**
	 * 根据任务ID获得任务实例
	 * 
	 * @param taskId
	 *            任务ID
	 * @return
	 * @throws Exception
	 */
	private TaskEntity findTaskById(String taskId){
		TaskEntity task = (TaskEntity) taskService.createTaskQuery()
				.taskId(taskId).singleResult();
		return task;
	}
	
	/**
	 * 根据任务ID获取对应的流程实例
	 * 
	 * @param taskId
	 *            任务ID
	 * @return
	 * @throws Exception
	 */
	public ProcessInstance findProcessInstanceByTaskId(String taskId)
			throws Exception {
		// 找到流程实例
		ProcessInstance processInstance = runtimeService
				.createProcessInstanceQuery()
				.processInstanceId(findTaskById(taskId).getProcessInstanceId())
				.singleResult();
		return processInstance;
	}

	/**
	 * 获取RepositoryService
	 * @return
	 */
	public RepositoryService getRepositoryService() {
		return repositoryService;
	}

	/**
	 * 获取RuntimeService
	 * @return
	 */
	public RuntimeService getRuntimeService() {
		return runtimeService;
	}

	/**
	 * 获取TaskService
	 * @return
	 */
	public TaskService getTaskService() {
		return taskService;
	}

	/**
	 * 获取IdentityService
	 * @return
	 */
	public IdentityService getIdentityService() {
		return identityService;
	}

	/**
	 * 获取HistoryService
	 * @return
	 */
	public HistoryService getHistoryService() {
		return historyService;
	}
}
